package com.gmadmin.sysadmin.security.filter;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWTUtil;
import com.gmadmin.sysadmin.security.config.SecurityProperties;
import com.gmadmin.sysadmin.security.dto.AuthorityDto;
import com.gmadmin.sysadmin.security.dto.OnlineUserDto;
import com.gmadmin.sysadmin.security.dto.TokenUserDto;
import com.gmadmin.sysadmin.security.dto.UserDetailsDto;
import com.gmadmin.sysadmin.security.service.OnlinePermsService;
import com.gmadmin.sysadmin.security.service.OnlineUserService;
import com.gmadmin.sysadmin.security.utils.RedisUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.encrypt.KeyStoreKeyFactory;
import org.springframework.stereotype.Component;

import java.security.KeyPair;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class TokenProvider {

    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private SecurityProperties properties;
    @Autowired
    private OnlineUserService onlineUserService;
    @Autowired
    private OnlinePermsService  onlinePermsService;

    public KeyPair keyPair() {
        //从classpath下的证书中获取秘钥对
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "123456".toCharArray());
        return keyStoreKeyFactory.getKeyPair("jwt", "123456".toCharArray());
    }

    /**
     * 创建Token 设置永不过期，
     * Token 的时间有效性转到Redis 维护
     */
    public String createToken(UserDetailsDto user) {
        byte[] encoded = keyPair().getPrivate().getEncoded();
        Integer id = user.getUser().getId();
        String username = user.getUser().getUsername();
        Map<String, Object> body = new HashMap<>();
        long timeMillis = System.currentTimeMillis()/1000;
        body.put("id", id);
        body.put("currentTimeInSeconds", timeMillis);
        body.put("tokenValidityInSeconds", properties.getTokenValidityInSeconds());
        body.put("username", username);
        body.put("uuid", IdUtil.simpleUUID());
        return JWTUtil.createToken(body, encoded);
    }

    /**
     *
     * 依据Token 获取鉴权信息
     *
     * @param token token
     * @return Authentication
     */
    Authentication getAuthentication(String token) {
        TokenUserDto tokenUserDto = getUser(token);
        String userInfoRedisKey = loginKey(tokenUserDto);
        OnlineUserDto onlineUserDto = onlineUserService.getOne(userInfoRedisKey);
        UserDetailsDto userDetailsDto = onlineUserDto.userDetailsDto();
        List<AuthorityDto> permsDto = onlinePermsService.findPermsDto(userDetailsDto.getRoles());
        return new UsernamePasswordAuthenticationToken(userDetailsDto, token, permsDto);
    }

    /**
     * @param token 需要检查的token
     */
    public void checkRenewal(String token) {
        // 判断是否续期token,计算token的过期时间
        String loginKey = loginKey(token);
        long time = redisUtils.getExpire(loginKey) * 1000;
        Date expireDate = DateUtil.offset(new Date(), DateField.MILLISECOND, (int) time);
        // 判断当前时间与过期时间的时间差
        long differ = expireDate.getTime() - System.currentTimeMillis();
        // 如果在续期检查的范围内，则续期
        if (differ <= properties.getDetect()) {
            long renew = time + properties.getRenew();
            redisUtils.expire(loginKey, renew, TimeUnit.MILLISECONDS);
        }
    }

    /**
     * 从token获取在线用户信息
     */
    public OnlineUserDto getOnlineUserDto(String token){
        TokenUserDto tokenUserDto = getUser(token);
        String userInfoRedisKey = loginKey(tokenUserDto);
        return onlineUserService.getOne(userInfoRedisKey);
    }

    public String getToken(HttpServletRequest request) {
        final String requestHeader = request.getHeader(properties.getHeader());
        if (requestHeader != null && requestHeader.startsWith(properties.getTokenStartWith())) {
            return requestHeader.substring(7);
        }
        return null;
    }

    /**
     * 从token中获取登录用户信息
     * @param token 用户登录token
     * @return 用户信息
     */
    public TokenUserDto getUser(String token) {
        String userStr = JWTUtil.parseToken(token).getPayload().toString();
        return JSONUtil.toBean(userStr, TokenUserDto.class);
    }

    /**
     * 获取登录用户RedisKey
     * @param tokenUserDto 用户登录信息
     * @return key
     */
    public String loginKey(TokenUserDto tokenUserDto) {
        return properties.getOnlineKey() + tokenUserDto.username() + ":" + tokenUserDto.uuid();
    }

    /**
     * 获取登录用户RedisKey
     * @param token 用户登录token
     * @return key
     */
    public String loginKey(String token) {
        TokenUserDto tokenUserDto = getUser(token);
        return properties.getOnlineKey() + tokenUserDto.username() + ":" + tokenUserDto.uuid();
    }

}
